//
// Created by ta&to on 2022/8/16.
//

#include "tcp_socket.h"

static inline int __socket_create(log_t *logt, int __family, int __type, int __protocol) {
    int socket_fd;
    if((socket_fd = socket(__family, __type, __protocol)) < 0) {
        error(logt,"socket_server to create a TCP socket fd failure:[%s]", strerror(errno));
        return -1;
    }
    return socket_fd;
}

static inline int __socket_option(log_t *logt, int __socket_fd, int __level, int __optname, const void *optval,
                                  socklen_t __optlen) {
    int optionid;
    if(__socket_fd < 0)  return -1;
    if((optionid = setsockopt(__socket_fd, __level, __optname, optval, __optlen)) < 0) {
        error(logt,"setsockopt failure.%s", strerror(errno));
        return -2;
    }
    return optionid;
}

static inline int __socket_bind(log_t *logt, int __socket_fd, const short __sa_family, const char *__listen_ip, const int __listen_port) {
    int bindid;
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    //设置协议
    server_addr.sin_family = ISNULL(__sa_family)?AF_INET:__sa_family;
    //设置Port
    server_addr.sin_port = htons(__listen_port);
    if(!__listen_ip) {   //设置IP
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else {
        server_addr.sin_addr.s_addr = htonl(__listen_ip);
    }
    memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero));
    if((bindid = bind(__socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0) {
        error(logt, "socket[%d] bind on port[%d] for ip address failure:%s", __socket_fd , __listen_port, strerror(errno));
        return -3;
    }
    return bindid;
}

static inline int __socket_listen(log_t *logt, int __socket_fd, int __n) {
    int listenid;
    if((listenid = listen(__socket_fd, __n)) < 0)
    {
        error(logt,"socket[%d] listen() fail:%s", __socket_fd, strerror(errno));
        return -4;
    }
    return listenid;
}

static inline int __socket_accept(log_t *logt, int __socket_fd, struct sockaddr *__peer, socklen_t *__len) {
    int client_fd = -1;
    if((client_fd = accept(__socket_fd, __peer, __len)) < 0) {
        error(logt,"accept new client failure:%s", strerror(errno));
        return -5;
    }
    return client_fd;
}
// 发送消息
static inline int __send(log_t *logt, int __client_fd, char* buf, int len) {
    int rv = send(__client_fd, buf, len, 0);
    return rv;
}
// 接收消息
static inline int __resv(log_t *logt, int __client_fd, char* buf, int len) {
    int rv = recv(__client_fd, buf, len, 0);
    return rv;
}
static inline void __closeAll(log_t *logt, int __socket_fd, int __client_fd) {
    if(ISNOTNULL(__client_fd)) close(__client_fd);
#if defined(__MINGW32__) || defined(__MINGW64__)
    //Winsows下关闭socket
    if(ISNOTNULL(__socket_fd)) closesocket(__socket_fd);
    WSACleanup();
#else
    if(ISNOTNULL(__socket_fd)) close(__socket_fd);
#endif
    if(ISNOTNULL(__socket_fd)) exit(0);
}
static int _socket_server_init(log_t *logt, char *listen_ip, int listen_port, int __family, int __type, int __protocol,
                               int __optlevel, int __optname, const void *optval,
                               socklen_t __optlen, int n) {
    int socket_fd;
    if((socket_fd = __socket_create(logt, __family, __type, __protocol)) < 0) return 0;
    info(logt, "socket[%d] create success", socket_fd);
    if(__socket_option(logt, socket_fd, __optlevel, __optname, optval, __optlen) < 0) return 0;
    if((__socket_bind(logt, socket_fd, __family, listen_ip, listen_port)) < 0) return 0;
    info(logt, "socket[%d] bind on port[%d] for ip address success", socket_fd, listen_port);
    if(__socket_listen(logt, socket_fd, n) < 0) return 0;
    return socket_fd;
}

static int _createTcpSocket(log_t *logt, char *listen_ip, int listen_port, int link_num)
{
    int on = 1;
#if defined(__MINGW32__) || defined(__MINGW64__)
    //Winsows下启用socket
    int nRes;
    WSADATA wsadata;
    if(WSAStartup(MAKEWORD(2,2),&wsadata)==SOCKET_ERROR)
    {
      info(logt,"WSAStartup() fail");
      exit(0);
    }

    if (0 != nRes)
    {
        switch (nRes)
        {
            case WSASYSNOTREADY:
                error(logt, "可以重启电脑，或检查网络库");
                break;
            case WSAVERNOTSUPPORTED:
                error(logt, "请更新网络库");
                break;
            case WSAEINPROGRESS:
                error(logt, "Please reboot this software");
                break;
            case WSAEPROCLIM:
                error(logt, "请关闭不必要的软件，以为当前网络提供充足资源");
                break;
            case WSAEFAULT:
                error(logt, "参数错误");
                break;
        }
    }
    //版本校验
    if (2 != HIBYTE(wsadata.wVersion) || 2 != LOBYTE(wsadata.wVersion))
    {
        //版本打开错误
        error(logt, "WSAStartup 初始化失败：版本打开错误");
        CloseAll(logt, 0, 0);		//关闭网络库
        return 0;
    }
#endif
    return _socket_server_init(logt, listen_ip, listen_port, AF_INET, SOCK_STREAM,
                               0, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on), link_num);
}

int __socket_set_block(log_t *logt, int fd, int non_block) {
#if !defined(__MINGW32__) && !defined(__MINGW64__)
    int flags;

    /* Set the socket blocking (if non_block is zero) or non-blocking.
     * Note that fcntl(2) for F_GETFL and F_SETFL can't be
     * interrupted by a signal. */
    if ((flags = fcntl(fd, F_GETFL)) == -1) {
        return -1;
    }

    /* Check if this flag has been set or unset, if so,
     * then there is no need to call fcntl to set/unset it again. */
    if (!!(flags & O_NONBLOCK) == !!non_block)
        return 1;

    if (non_block)
        flags |= O_NONBLOCK;
    else
        flags &= ~O_NONBLOCK;

    if (fcntl(fd, F_SETFL, flags) == -1) {
        return -1;
    }
    return 0;
#else
    return -1;
#endif
}

int socketNonBlock(log_t *logt, int fd) {
    return __socket_set_block(logt, fd,1);
}

int socketBlock(log_t *logt, int fd) {
    return __socket_set_block(logt, fd,0);
}

int CreateTcpSocket(log_t *logt, char *listen_ip, int listen_port, int link_num)
{
    return _createTcpSocket(logt, listen_ip, listen_port, link_num);
}

int Accept(log_t *logt, int socket_fd, struct sockaddr *saddr, socklen_t *slen) {
    return __socket_accept(logt, socket_fd, saddr, slen);
}

int putData(log_t *logt,const int client_fd, char* buf, int len) {
    return __send(logt, client_fd, buf, len);
}

int getData(log_t *logt, const int client_fd, char* buf, int len) {
    return __resv(logt, client_fd, buf, len);
}

void CloseAll(log_t *logt, int socket_fd, int client_fd) {
    __closeAll(logt, socket_fd, client_fd);
}